package com.video.file.utils;

import com.video.file.config.MinIOConfig;
import com.video.file.config.MinIOConfigProperties;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.errors.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.*;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author shang tf
 * @version 1.0
 * @data 2023/12/22 9:57
 */
@Component
public class FileCoverUtils {

    @Autowired
    private MinIOConfigProperties minioConfig;

    @Autowired
    private MinioClient minioClient;

    // 执行成功0,失败1
    private int CODE_SUCCESS = 0;
    private int CODE_FAIL = 1;
    // 下载到本地的地址（"D:\\local\\m3u8"或"/opt/m3u8temp"）
    @Value("C:\\Users\\admin\\Desktop\\m3u8")
    private String localDirPath;

    public Map uploadM3U8(String remoteVideoUrl) throws ServerException, InvalidBucketNameException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        String fileDate = "";
        String fileName = "";
        String fileNameNotSuffix = "";
        try {
            Pattern pattern = Pattern.compile("\\d{4}/\\d{2}/\\d{2}");
            Matcher matcher = pattern.matcher(remoteVideoUrl);
            if (matcher.find()) {
                fileDate = matcher.group();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            URL fileUrl = new URL(remoteVideoUrl);
            fileName = fileUrl.getFile();
            fileName = fileName.substring(fileName.lastIndexOf("/") + 1);
            String filePath = fileUrl.getPath();
            fileNameNotSuffix = filePath.substring(filePath.lastIndexOf("/") + 1);
            fileNameNotSuffix = fileName.substring(0, fileName.lastIndexOf("."));
        } catch (Exception e) {
            e.printStackTrace();
        }
        String localVideoPath = localDirPath + File.separator + fileNameNotSuffix + File.separator + fileName;
        // 将mp4文件转为ts文件
        String cmd_mp4_2_ts = " -y -i " + localDirPath + File.separator + fileNameNotSuffix + File.separator + fileName + " -vcodec copy -acodec copy -vbsf h264_mp4toannexb " + localDirPath + File.separator + fileNameNotSuffix + File.separator + fileNameNotSuffix + "_ts.ts ";
        // 将ts文件分割
        String cmd_ts_split = " -i " + localDirPath + File.separator + fileNameNotSuffix + File.separator + fileNameNotSuffix + "_ts.ts" + " -c copy -map 0 -f segment -segment_list " + localDirPath + File.separator + fileNameNotSuffix + File.separator + fileNameNotSuffix + "_m3u8.m3u8" + " -segment_time 15 " + localDirPath + File.separator + fileNameNotSuffix + File.separator + fileNameNotSuffix + "_15s_%3d.ts ";

        CompletableFuture<Integer> completableFutureTask = CompletableFuture.supplyAsync(() -> {
            try {
                downloadVideo(remoteVideoUrl, localVideoPath);
                cmdExecut(cmd_mp4_2_ts);
                return CODE_SUCCESS;
            } catch (Exception e) {
                e.printStackTrace();
                return CODE_FAIL;
            }
        }, ThreadPoolExecutorUtils.pool).thenApplyAsync((Integer code) -> {

            if (CODE_SUCCESS != code) {
                return CODE_FAIL;
            }
            System.out.println("第一步：视频整体转码ts,成功！");
            Integer codeTmp = cmdExecut(cmd_ts_split);
            if (CODE_SUCCESS != codeTmp) {
                return CODE_FAIL;
            }
            System.out.println("第二步：ts 文件切片,成功！");
            return codeTmp;
        }, ThreadPoolExecutorUtils.pool);

        try {
            System.out.println(String.format("获取最终执行结果:%s", completableFutureTask.get() == CODE_SUCCESS ? "成功！" : "失败！"));
            // 切片成功后上传远程仓库
            String fileRemote = fileDate + "/" + fileNameNotSuffix + "/";
            Map<String, String> urls = new HashMap<>();
            String url = uploadM3u8ToRemoteStorage(fileRemote, localDirPath + File.separator + fileNameNotSuffix);
            urls.put("videoUrl", url);
            System.out.println("-------------:"+ fileRemote);
            urls.put("fileRemote",fileRemote);
            return urls;
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
        return null;
    }


    public void downloadVideo(String remoteUrl, String localPath) throws IOException {
        URL url = new URL(remoteUrl);
        // 如果此文件夹不存在则创建文件夹
        File file = new File(localPath);
        String directoryPath = file.getParent();
        File directory = new File(directoryPath);
        if (!directory.exists()) {
            directory.mkdirs();
        }
        // 将远程链接的文件下载到本地
        try (InputStream in = url.openStream()) {
            Files.copy(in, Paths.get(localPath));
        }
    }

    /**
     * @throws
     * @Description: (执行ffmpeg自定义命令)
     * @param: @param cmdStr
     * @param: @return
     * @return: Integer
     */
    public Integer cmdExecut(String cmdStr) {
        // code=0表示正常
        Integer code = null;
        FfmpegCmd ffmpegCmd = new FfmpegCmd();

        /**
         * 错误流
         */
        InputStream errorStream = null;
        try {
            // destroyOnRuntimeShutdown表示是否立即关闭Runtime
            // 如果ffmpeg命令需要长时间执行，destroyOnRuntimeShutdown = false

            // openIOStreams表示是不是需要打开输入输出流:
            //	       inputStream = processWrapper.getInputStream();
            //	       outputStream = processWrapper.getOutputStream();
            //	       errorStream = processWrapper.getErrorStream();
            ffmpegCmd.execute(false, true, cmdStr);
            errorStream = ffmpegCmd.getErrorStream();

            // 打印过程
            int len = 0;
            while ((len = errorStream.read()) != -1) {
                System.out.print((char) len);
            }

            // code=0表示正常
            code = ffmpegCmd.getProcessExitCode();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            ffmpegCmd.close();
        }
        // 返回
        return code;
    }

    public String uploadM3u8ToRemoteStorage(String fileRemote, String fileDir) throws IOException, ServerException, InvalidBucketNameException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        // 上传TS文件到S3
        File tsFilesDir = new File(fileDir);
        try {
            File[] tsFiles = tsFilesDir.listFiles();
            String m3u8FileLink = "";
            for (File tsFile : tsFiles) {
                if (tsFile.getName().contains(".ts") || tsFile.getName().contains(".m3u8")) {
                    String remoteFilePath = fileRemote + tsFile.getName();
                    FileInputStream fis = new FileInputStream(tsFile);
                    if (tsFile.getName().contains(".m3u8")) {
                        m3u8FileLink = minioConfig.getEndpoint() + "/" + minioConfig.getBucket() + remoteFilePath;
                        updateM3u8FileContent(tsFile, fileRemote);
                    }
                    minioClient.putObject(
                            PutObjectArgs.builder()
                                    .bucket(minioConfig.getBucket())
                                    .object(remoteFilePath)
                                    .stream(fis, tsFile.length(), -1)
                                    .contentType(getContentType(tsFile))
                                    .build());
                    System.out.println("ceshi::"+fileRemote);
                    fis.close();
                }
            }
            return m3u8FileLink;
        } finally {
            deleteDirectory(tsFilesDir);
        }
    }

    /*
     * 修改ts 内部的播放路径
     * */
    private void updateM3u8FileContent(File m3u8File, String fileRemote) throws IOException {
        StringBuilder content = new StringBuilder();
        try (BufferedReader reader = new BufferedReader(new FileReader(m3u8File))) {
            String line;
            while ((line = reader.readLine()) != null) {
                if (line.endsWith(".ts")) {
                    // 假设每行格式为 "#EXTINF:duration,segment_name.ts"
                    content.append(minioConfig.getEndpoint()).append("/").append(minioConfig.getBucket()).append(fileRemote).append(line).append("\n");
                } else {
                    content.append(line).append("\n");
                }
            }
        }

        try (FileWriter writer = new FileWriter(m3u8File)) {
            writer.write(content.toString());
        }
    }


    // 判断上传minio类型
    private String getContentType(File file) {
        String fileName = file.getName().toLowerCase();
        if (fileName.endsWith(".ts")) {
            return "video/mp2t";
        } else if (fileName.endsWith(".m3u8")) {
            return "application/x-mpegURL";
        } else {
            return "application/octet-stream";
        }
    }

    // 删除生成的文件及目录
    private void deleteDirectory(File directory) {
        if (directory.exists()) {
            File[] files = directory.listFiles();
            if (files != null) {
                for (File file : files) {
                    if (file.isDirectory()) {
                        deleteDirectory(file);
                    } else {
                        file.delete();
                    }
                }
            }
            directory.delete();
        }
    }
}

